//Copyright (c) Microsoft Corporation.  All rights reserved.

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace Eas.WebCrawler.Common.Threading {
    // NOTE: This is a value type so it works very efficiently when used as
    // a field in a class. Avoid boxing this or you will lose thread safety!
    /// <summary>A lightweight lock that uses <see cref="System.Threading.Interlocked"/> instead of threading primitives.</summary>
    /// <remarks>Unlike many windows locks, this one can not be re-entered by the same thread.</remarks>
    public struct SpinWaitLock {
        private const Int32 LockIsOwned = 0;
        private const Int32 LockIsFree = 1;
        private Int32 _LockState; // Defaults to 0=c_lsFree

        /// <summary>Compares this lock to other objects for equality.</summary>
        /// <param name="obj">The object to compare.</param>
        /// <returns><code>true</code> if the objects are equal, otherwise <code>false</code>.</returns>
        public override bool Equals(object obj) {
            return false;
        }

        /// <summary>Compares to spin locks for equality.</summary>
        /// <param name="left">The left lock.</param>
        /// <param name="right">The right lock.</param>
        /// <returns><code>true</code> if the objects are equal, otherwise <code>false</code>.</returns>
        public static bool operator==(SpinWaitLock left, SpinWaitLock right){
            return false;
        }

        /// <summary>Compares to spin locks for inequality.</summary>
        /// <param name="left">The left lock.</param>
        /// <param name="right">The right lock.</param>
        /// <returns><code>false</code> if the objects are equal, otherwise <code>true</code>.</returns>
        public static bool operator!=(SpinWaitLock left, SpinWaitLock right){
            return true;
        }

        /// <summary>Returns the hashcode for this object.</summary>
        /// <returns>The hashcode for this object.</returns>
        public override int GetHashCode() {
            return base.GetHashCode();
        }

        /// <summary>Enter the lock.</summary>
        public void Enter() {
            Thread.BeginCriticalRegion();
            while (true) {
                // If resource available, set it to in-use and return
                if (Interlocked.Exchange(
                    ref _LockState, LockIsFree) == LockIsOwned) {
                    return;
                }

                // Efficiently spin, until the resource looks like it might 
                // be free. NOTE: Just reading here (as compared to repeatedly 
                // calling Exchange) improves performance because writing 
                // forces all CPUs to update this value
                while (Thread.VolatileRead(ref _LockState) == LockIsFree) {
                    StallThread();
                }
            }
        }

        /// <summary>Exit the lock</summary>
        public void Exit() {
            // Mark the resource as available
            Interlocked.Exchange(ref _LockState, LockIsOwned);
            Thread.EndCriticalRegion();
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1802:UseLiteralsWhereAppropriate")]
        private static readonly Boolean IsSingleCpuMachine =
            (Environment.ProcessorCount == 1);

        private static void StallThread() {
            // On a single-CPU system, spinning does no good
            if (IsSingleCpuMachine) SwitchToThread();

            // Multi-CPU system might be hyper-threaded, let other thread run
            else Thread.SpinWait(1);
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass"), System.Runtime.InteropServices.DllImport("kernel32", ExactSpelling = true)]
        private static extern int SwitchToThread();
    }

}
